home *** CD-ROM | disk | FTP | other *** search
Java Source | 1996-10-04 | 13.2 KB | 422 lines |
- package sub_arctic.anim;
- import sub_arctic.input.*;
- import sub_arctic.lib.*;
-
- import java.awt.Event;
- import java.util.Vector;
-
- /**
- * This is the dispatch agent which handles animation input events and
- * dispatches them to interface objects via the animatable input protocol
- * interface. Animation events are generated by a single instance of
- * the anim_generator class that runs in its own thread.<p>
- *
- * @author Ian Smith
- */
- public class animation_agent extends dispatch_agent {
-
- /* . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . */
-
- /**
- * This is the magic value for animation events.<p>
- *
- * Editorial comment by Ian:<br>
- * Note that Event.MISC_EVENT is PRIVATE because AWT is too
- * stupid to let anyone do anything useful. Further, they
- * add constants to it to derive MISC_EVENT values (which
- * is still broken if you are user-level code, since they
- * can add new events and not tell you, stepping on your
- * event numbers) so it appears that is the "right" way
- * to create new event codes, but you can't access it.
- * Had they been the least bit clever, they would have
- * realized that a static method called "firstUserCodeNumber"
- * would be the thing to do, preserving backward
- * compatibility and letting users do clever things.
- * Stupid, stupid, stupid.<p>
- */
- static final int ANIMATION_EVENT= 1000 + 10;
-
- /* . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . */
-
- /**
- * This is one of the three queues for use with this agent. The "unknown"
- * queue is for transitions whose start or end times are as yet unknown.
- * The "pending" queue is for transitions which have known start and
- * end times, but are not yet due to run. The "running" queue is for
- * the transitions that are currently executing.
- */
- protected Vector unknown;
-
- /**
- * This is one of the three queues for use with this agent. The "unknown"
- * queue is for transitions whose start or end times are as yet unknown.
- * The "pending" queue is for transitions which have known start and
- * end times, but are not yet due to run. The "running" queue is for
- * the transitions that are currently executing.
- */
- protected Vector pending;
-
- /**
- * This is one of the three queues for use with this agent. The "unknown"
- * queue is for transitions whose start or end times are as yet unknown.
- * The "pending" queue is for transitions which have known start and
- * end times, but are not yet due to run. The "running" queue is for
- * the transitions that are currently executing.
- */
- protected Vector running;
-
- /* . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . */
-
- /**
- * Initialize this agent. This is done automatically, users should
- * not have to construct this object.
- */
- public animation_agent() {
- /* create the queues */
- unknown=new Vector();
- pending=new Vector();
- running=new Vector();
-
- /* we don't need ticks at first */
- anim_generator.set_need_ticks(false);
- }
-
- /* . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . */
-
- /**
- * Handle an animation event. These periodically get put in the event
- * "queue" by the animation agent and picked up here.<p>
- *
- * @param event evt the event to dispatch
- * @param Object user_info the user information passed to the agent when the
- * user object joined the focus set
- * @param interactor to_obj the object to dispatch to
- * @param int seq_num the sequence number of this event
- * @return boolean true if one of our animatable objects handled the event
- */
- public boolean dispatch_event(event evt,Object user_info,interactor to_obj,
- int seq_num) {
-
- boolean work_to_do=true;
- long now=((Long)evt.arg()).longValue();
- int i;
- transition t;
-
- /* while there is more to do */
- while (work_to_do) {
-
- /* reset flag, it becomes true if we move someone to the active list */
- work_to_do=false;
-
- /* is there anyone on the pending list that is now eligible to run */
- for (i=0; i<pending.size(); ++i) {
-
- /* if this guy is now eligible, start him */
- t=(transition)pending.elementAt(i);
- if (t.interval().start_time()<=now) {
-
- /* he's eligible, start him and put him on the active list*/
- t.start(evt,user_info);
- pending.removeElement(t);
-
- /* this list is not sorted */
- running.addElement(t);
-
- /* if there are people on the unknown list that are dependent on
- this guy starting, we now want to move them to the pending
- list */
- work_to_do=dependent_on(time_interval.AFTER_START_OF,t,now);
-
- /* if work to do is now true, we'll need to just restart this
- whole iteration through, since the pending list changed*/
- if (work_to_do) {
- break;
- }
-
- /* we just fall through to the next item on the pending list */
- } else {
- /* since this list is sorted, we can break out as soon as we
- find a miss */
- break;
- }
- }
- }
-
- /* at this point everybody who is eligible to run on this tick is on the
- running list ... that list is not sorted and we'll need to ping
- everybody on it, so we just traverse it*/
- for (i=0; i<running.size(); ++i) {
-
- t=(transition)running.elementAt(i);
-
- /* send the message */
- t.step(evt,user_info,now);
-
- /* is he done? */
- if (t.finished()) {
-
- /* ok, he's done... anybody waiting on that trans to end? */
- dependent_on(time_interval.AFTER_END_OF,t,now);
-
- /* Possible problem: If someone is waiting on the end of that
- transition and their start time is 0 msec after it, we
- will wait until the next "tick" before we see them in
- the pending list and they get moved to the running list.
- At some level, this seems ok since they logically "follow"
- the redraw of the end of that transition. */
- /* now send it the "end" transition */
- t.end(evt,user_info);
-
- /* get it out of the run queue */
- running.removeElement(t);
- }
- }
-
- /* do we turn the generator off ? */
- if ((unknown.size()==0) &&
- (pending.size()==0) &&
- (running.size()==0)) {
- /* tell the generator we don't need it */
- anim_generator.set_need_ticks(false);
- }
-
- /* there are probably no other animation agents, but just in case we
- * let this event go on to them... */
- return false;
- }
-
- /* . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . */
-
- /**
- * Is this event one we care about (an ANIMATION_EVENT)?
- * @param event evt the event to test
- * @return boolean true if we are interested in this event
- */
- public boolean event_is_useful(event evt) {
- return (evt.id()==ANIMATION_EVENT);
- }
-
- /* . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . */
-
- /**
- * Schedule a transition on behalf of some interactor.<P>
- *
- * This is the principal API for user level code to use to schedule
- * animations. You should pass the transition to this function and the
- * agent will set things up for you.<p>
- *
- * @param transition t the transition you want executed (this contains a
- * reference to the animatable object in the transition)
- */
- public void schedule_transition(transition t) {
-
- /* tell the generator we need ticks... */
- anim_generator.set_need_ticks(true);
-
- /* check to see if it is already valid */
- if (t.interval().valid()) {
- insert_into_pending(t);
- } else {
-
- /* might have specified the start time and put a duration */
- if (t.interval().specified_start() && t.interval().used_duration()) {
- t.interval().set_ending_time(t.interval().start_time() +
- t.interval().duration());
-
- /* its now ready */
- insert_into_pending(t);
-
- } else {
-
- /* put it in the unknown list, its not ready yet... must be related to
- some other object... sanity check first*/
- if (t.interval().relative_to_others()==false) {
- throw new sub_arctic_error("Insufficiently specified transition "+
- "rejected by animation_agent!");
- } else {
- /* its ok */
- unknown.addElement(t);
- }
- }
- }
- }
-
- /* . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . */
-
- /**
- * This method puts a new transition in the pending list. This list is
- * ordered by start time.
- *
- * @param transition t the transition to insert
- */
- protected void insert_into_pending(transition t) {
- int i;
- transition tmp;
-
- /* loop over the pending vector */
- for (i=0; i<pending.size(); ++i) {
- tmp=(transition)pending.elementAt(i);
-
- /* if its less than the one we are looking at ...*/
- if (t.interval().start_time()<tmp.interval().start_time()) {
-
- /* insert in that elements spot */
- pending.insertElementAt(t,i);
-
- /* we're done */
- return;
- }
- }
-
- /* if we are here, then it is larger than everything on the list */
- /* so add at the end */
- pending.addElement(t);
- }
-
- /* . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . */
-
- /**
- * This method moves things from the unknown list to the pending list.
- * As it does it, it fills in the values that might be necessary for this
- * object to be fully ready.
- *
- * @param int which should be one of time_interval.AFTER_START_OF and
- * time_interval.AFTER_END_OF
- * @param transition target the candidate transition for being depended upon
- * @param long now what time is it
- * @return boolean true if someone was dependent on the target transition
- */
- boolean dependent_on(int which,transition target,long now) {
- int i;
- boolean returnvalue=false;
- transition tmp;
- time_interval interval;
-
- /* loop over the list of objects which are not yet rich are not yet ready */
- for (i=0; i<unknown.size(); ++i) {
-
- /* get the ith element */
- tmp=(transition)unknown.elementAt(i);
-
- /* get its interval */
- interval=tmp.interval();
-
- /* if its related to this object by the which we need to move it */
- if ((interval.related_to()==target) &&
- (interval.related_how()==which)) {
-
- /* remove it from the list of unknowns */
- unknown.removeElement(tmp);
-
- /* fill in the values */
- interval.set_start_time(now+(interval.delay()));
-
- /* they may have used a duration, if so decode it now */
- if (interval.used_duration()) {
- interval.set_ending_time(interval.start_time() +
- interval.duration());
- }
-
- /* ok, its now a valid entry */
- if (!interval.valid()) {
- throw new sub_arctic_error("Invalid time interval constructed:"+
- "You probably forget to supply an "+
- "ending time or a duration");
- }
-
- /* put the whole transition in the list */
- insert_into_pending(tmp);
-
- /* we have now added something to the pending list, so we
- need to inform the caller */
- returnvalue=true;
- }
- }
- return returnvalue;
- }
-
- /* . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . */
-
- /**
- * This function causes the animation agent to terminate a transition.
- * A transition may be terminated even before it has begun. If you
- * terminate a running transition, other transitions which are
- * dependent on it ending are triggered. If you terminate a transition
- * which has not yet begun, other pending transitions which are
- * dependent on this transition starting or ending are NOT triggered.
- * Attempting to remove a transition which is neither pending
- * nor running has no effect. Currently, this does not result
- * in call to the end_transition method of your animatable object.
- * The reason it current doesn't do this is to make sure
- * that people who are just using the end_transition message
- * as a trigger for time-based events do not receive this call.
- *
- * @param transition target the transition you want removed.
- */
- public void remove_transition(transition target) {
- int i;
- transition t;
-
- /* walk down the running list */
- for (i=0; i<running.size(); ++i) {
-
- t=(transition)running.elementAt(i);
-
- /* is it there? */
- if (t.equals(target)) {
-
- /* found it */
- running.removeElementAt(i);
-
- /* possible modifications of the pending list */
- dependent_on(time_interval.AFTER_END_OF,t,time_interval.now());
-
- /* we are done */
- return;
- }
- }
-
- /* walk down the pending list */
- for (i=0; i<pending.size(); ++i) {
-
- t=(transition)pending.elementAt(i);
-
- /* is it there? */
- if (t.equals(target)) {
- pending.removeElementAt(i);
- }
- }
-
- /* walk down the unknown list */
- for (i=0; i<unknown.size(); ++i) {
-
- t=(transition)unknown.elementAt(i);
-
- /* is it there? */
- if (t.equals(target)) {
- unknown.removeElementAt(i);
- }
- }
- /* we're done */
- }
-
- /* . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . */
-
- }
- /*=========================== COPYRIGHT NOTICE ===========================
-
- This file is part of the subArctic user interface toolkit.
-
- Copyright (c) 1996 Scott Hudson and Ian Smith
- All rights reserved.
-
- The subArctic system is freely available for most uses under the terms
- and conditions described in
- http://www.cc.gatech.edu/gvu/ui/sub_arctic/sub_arctic/doc/usage.html
- and appearing in full in the lib/interactor.java source file.
-
- The current release and additional information about this software can be
- found starting at: http://www.cc.gatech.edu/gvu/ui/sub_arctic/
-
- ========================================================================*/
-